// scaup - Scala up!
// (c) 2009, Normen Müller, normen.mueller@gmail.com  
// $Id: Parser.scala 86 2010-05-11 21:32:13Z normen.mueller $
package locutor.rws.rs.parsing

import java.io.File

import collection.mutable.HashSet

import scaup.Views._

import locutor.rws.gm._
import locutor.rws.rs._
import locutor.util.parsing._

/** A rule system parser.
 * 
 *  <p>At the moment only primitive string representations of rule systems are supported. No need for sophisticated AST yet due to direct looping into GrGEN.</p>
 *  <p>Note the fact that we can't parse HEX numbers as well as ``#include'', yet</p>
 * 
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.4
 */
private [rs] class Parser extends XGRSParser with ExpressionParser {
  /* Fill in abstract defs
   */
  type Tokens = Lexer
  
  val lexical: Tokens = new Lexer
  
  /* Configure lexical parsing
   */                                 
  lexical.reserved ++= new HashSet[String] ++ List("alternative", "delete", "dpo", "emit", "emitpre", "emitpost", "eval", "exact", "exec", "extends", "hom", "if",
    "independent", "induced", "iterated", "map", "modify", "multiple", "negative", "optional", "pattern", "patternpath", "replace", "return", "rule",
    "rulesystem", "set", "test", "var", "with", "@COLLECTION"
  ) ++ XGRSParser.reserved ++ ExpressionParser.reserved
  
  lexical.delimiters ++= new HashSet[String] ++ List("{", "}", "<-", "->", "?-", "-?", ";", "\\") ++ XGRSParser.delimiters ++ ExpressionParser.delimiters  
  
  /* Define the grammar
   */  
  lazy val rs = ("rulesystem" ~> ident) ~ opt(exts) ~ (model <~ "{") ~ (stmts <~ "}") ^^ {
    case name ~ exts ~ model ~ stmts => new RuleSystem(name, model, stmts) join (exts match {
      case Some(exts) => exts
      case None       => Nil
    })
  }
  
  private lazy val exts = ("extends" ~> (name ~ (("," ~> name)*))) ^^ {
    case u ~ us => (u :: us) map (r => RuleSystem.parse((r.toLowerCase + ".rs").toFile))
  }
  
  //private lazy val models = ("using" ~> (model ~ (("," ~> model)*))) <~ ";" ^^ {case u ~ us => (u :: us) map (r => GraphModel.parse((r.toLowerCase + ".gm").toFile))}
  private lazy val model =
    ( ("with" ~ "@COLLECTION") ^^^ {GraphModel.empty}
    | ("with" ~> name)          ^^ {case model => GraphModel.parse((model.toLowerCase + ".gm").toFile)}
    )
  
  private lazy val name = accept("string",  {case lexical.StringLit(n) => n})
  
  private lazy val stmts = (rule*) ^^ { case rules => rules }
  
  private lazy val rule = opt("exact" | "induced" | "dpo") ~ (test_stmt | rule_stmt | pattern_stmt) ^^ {
    case m ~ r => m match {
      case Some(m) => r match {
        case TestRule(name, decl)    => TestRule(name, m + " " + decl)
        case RewriteRule(name, decl) => TestRule(name, m + " " + decl)
        case Pattern(name, decl)     => Pattern(name, m + " " + decl)
      }
      case _       => r
    }
  }  
  
  private lazy val test_stmt = ("test" ~> ident) ~ opt(params) ~ opt(return_types) ~ ("{" ~> ((pattern)*) <~ "}") ^^ {
    case id ~ params ~ rts ~ ps => TestRule(id, "test " +  id + ∑(params) + ∑(rts) + " { " + ∑(ps) + " } ")
  }
  
  private lazy val rule_stmt = ("rule" ~> ident) ~ opt(params) ~ opt(return_types) ~ ("{" ~> ((pattern)*)) ~ ((replace | modify) <~ "}") ^^ {
    case id ~ params ~ rts ~ ps ~ rm => RewriteRule(id, "rule " + id + ∑(params) + ∑(rts) + " { " + ∑(ps) + rm + " } ")
  }
  
  private lazy val pattern_stmt = {
    ( ("pattern" ~> ident) ~ opt(params) ~ opt(return_types) ~ ("{" ~> ((pattern_body)*)) ~ (opt(replace | modify) <~ "}") ^^ {
      case id ~ params ~ rts ~ ps ~ rm => Pattern(Some(id), "pattern " + id + ∑(params) + ∑(rts) + " { " + ∑(ps) + ∑(rm) + " } ")
    }
    | pattern_body ^^ {
      case ps => Pattern(None, ps)
    }
    )
  }
  
  private lazy val pattern = (("pattern" ~ "{") ~> (((pattern_body)*) <~ "}") ^^ {case ps => "pattern { " + ∑(ps) + " } "}) | pattern_body
  
  private lazy val pattern_body: Parser[String] = graphlet | alternative | iterated | negative | independent | condition | return_stmt | hom | exact | induced
  
  private lazy val graphlet = (((node ~ opt(continuation)) ^^ {case n ~ c => n + ∑(c) + ";"}) | (continuation ^^ {case c => c + ";"})) <~ ";"
  
  private lazy val alternative = ("alternative" ~ "{") ~> ((alternative_body)+) <~ "}" ^^ {case abs => "alternative { " + ∑(abs) + " } "}
  
  private lazy val alternative_body = (ident <~ "{") ~ ((pattern_body)*) ~ (opt(replace | modify) <~ "}") ^^ {
    case id ~ ps ~ rm => id+" { " + ∑(ps) + ∑(rm) + " } "
  }
  
  private lazy val iterated = ("iterated" | "optional" | "multiple") ~ (opt(ident) <~ "{") ~ ((pattern_body)*) ~ (opt(replace | modify) <~ "}") ^^ {
    case m ~ id ~ ps ~ rm => m+" " + ∑(id) + " { " + ∑(ps) + ∑(rm) + " } "
  }
  
  //def negative = ("negative" ~ "{") ~> ((("patternpath" | "pattern") ~ ";")*) ~ (((pattern_body)*) <~ "}")
  private lazy val negative = ("negative" ~ "{") ~> (((pattern_body)*) <~ "}") ^^ { case pb => "negative { " + ∑(pb) + " } " }
  
  //def independent = "independent" ~ "{" ~ ((("patternpath" | "pattern") ~ ";")*) ~ ((pattern_body)*) ~ "}"
  private lazy val independent = ("independent" ~ "{") ~> (((pattern_body)*) <~ "}") ^^ { case ps => "independent { " + ∑(ps) + " } " }
  
  private lazy val condition = ("if" ~ "{") ~> (((expr <~ ";")*) <~ "}") ^^ { case exprs => "if { " + ((exprs map (_ + ";")) mkString) + " } " }
  
  private lazy val hom = ("hom" ~ "(") ~> (ident ~ ((("," ~> ident)*) <~ ")")) <~ ";" ^^ { case id ~ ids => (id :: ids) mkString ("hom(", ",", ");") }
  
  private lazy val exact = ("exact" ~ "(") ~> (ident ~ ((("," ~> ident)*) <~ ")")) <~ ";"  ^^ {case id ~ ids => (id :: ids) mkString ("exact(", ",", ");")}
  
  private lazy val induced = ("induced" ~ "(") ~> (ident ~ ((("," ~> ident)*) <~ ")")) <~ ";" ^^ {case id ~ ids => (id :: ids) mkString ("induced(", ",", ");")}
  
  private lazy val replace = ("replace" ~> opt(ident)) ~ opt(params) ~ ("{" ~> ((replace_body)*) <~ "}") ^^ {
    case id ~ ps ~ rbs => "replace " + ∑(id) + ∑(ps) + " { " + ∑(rbs) + " } "
  }
  
  private lazy val replace_body =  (graphlet | eval | return_stmt | exec | emit)
  
  private lazy val modify = ("modify" ~> opt(ident)) ~ opt(params) ~ ("{" ~> ((modify_body)*) <~ "}") ^^ {
    case id ~ ps ~ mbs => "modify " + ∑(id) + ∑(ps) + " { " + ∑(mbs) + " } "
  }
  
  private lazy val modify_body = (graphlet | eval | delete | return_stmt | exec | emit)
  
  private lazy val node =
    ( (opt(annotated_ident | annotation) <~ ":") ~ ( (ident ~ args) ^^ {case id ~ as => id+as}
                                                   | (ident | typeOf) ~ opt(type_constraint) ~ opt("<" ~> ident <~ ">") ^^ {
                                                     case id ~ tc ~ rt => id+ ∑(tc) + (rt match {case Some(rt) => "<"+rt+">" case _=> ""})
                                                   }
                                                   )                ^^ {case id ~ t  => ∑(id) + ":" + t}
    | ident ~ opt(args)                                             ^^ {case id ~ as => id + ∑(as)}
    | ("." ~> opt(annotation))                                      ^^ {case as      => "." + ∑(as)}
    )
  
  private lazy val edge = 
    ( ("<-" | "-") ~ opt(edge_refinement) ~ ("->" | "-") ^^ {case l ~ e ~ r => l + ∑(e) + r}
    | ("?-" ~ opt(edge_refinement) ~ "-?")               ^^ {case l ~ e ~ r => l + ∑(e) + r}
    )
  
  private lazy val edge_refinement = 
    ( (opt(annotated_ident) <~ ":") ~ (ident | typeOf) ~ opt(type_constraint) ~ opt("<" ~> ident <~ ">") ^^ {
      case v ~ t ~ tc ~ rt => ∑(v) + ":" + t + ∑(tc) + (rt match {case Some(rt) => "<"+rt+">" case _=> ""})
    }
    | ident
    )
  
  private lazy val continuation: Parser[String] = edge ~ opt(node ~ opt(continuation)) ^^ {
    case e ~ Some(n ~ Some(c)) => e+n+c
    case e ~ Some(n ~ None)    => e+n
    case e ~ None              => e
  }
  
  private lazy val return_stmt = ("return" ~ "(") ~> (expr ~ (("," ~> expr)*)) <~ (")" ~ ";") ^^ {
    case expr ~ exprs => (expr :: exprs) mkString ("return(", ",", ");")
  }
  
  private lazy val params = "(" ~> opt(param ~ (("," ~> param)*)) <~ ")" ^^ {
    case Some(p ~ ps) => (p :: ps) mkString ("(", "," , ")")
    case None         => "()"
  }
  
  private lazy val param = (edge | variable | node)
  
  private lazy val args = ("(" ~> opt(expr ~ (("," ~> expr)*))) <~ ")" ^^ {
    case Some(expr ~ exprs) => (expr :: exprs) mkString ("(", ",", ")")
    case None               => "()"
  }
  
  private lazy val type_constraint =
    ( "\\" ~> ident                              ^^ {case id  => "\\"+id}
    | "\\" ~> ("(" ~> repsep(ident, "+") <~ ")") ^^ {case ids => ids mkString ("\\(", "+", ")")}
    )
  
  private lazy val return_types = (":" ~ "(") ~> (opt((ident | primitives) ~ ((("," ~> (ident | primitives))*))) <~ ")") ^^ {
    case Some(t ~ ts) => (t :: ts) mkString (":(", ",", ")")
    case None         => ":()"
  }
  
  private lazy val variable = "var" ~> (ident <~ ":") ~
    ( ident | primitives
    | ("map" ~ "<") ~> (ident <~ ",") ~ (ident <~ ">") ^^ {case f ~ s => "map<"+f+","+"s"+">"}
    | ("set" ~ "<") ~> (ident <~ ">")                  ^^ {case f     => "set<"+f+">"}
    ) ^^ {case id ~ t => "var "+id+":"+t}
  
  private lazy val delete = ("delete" ~ "(") ~> (ident ~ (("," ~> ident)*)) <~ (")" ~ ";") ^^ {case id ~ ids => "delete("+((id :: ids) mkString ",")+");"}
  
  private lazy val exec = ("exec" ~ "(") ~> xgrs <~ (")" ~ ";") ^^ { case xgrs => "exec("+xgrs+");" }
  
  private lazy val emit = ("emit" | "emitpre" | "emitpost") ~ ("(" ~> expr) ~ (("," ~> expr)*) <~ (")" ~ ";") ^^ {
    case e ~ expr ~ exprs => e+"("+((expr :: exprs) mkString ",")+");"
  }
  
  private lazy val eval = ("eval" ~ "{") ~> (((assignment <~ ";")*) <~ "}") ^^ { case as => "eval { "+((as map (_ + ";")) mkString)+" } " }
  
  private lazy val assignment = (((ident <~ ".") ~ ident) ^^ {case c ~ m => c+"."+m} | visited) ~ ("=" ~> expr) ^^ { case l ~ r => l+"="+r }
  
  private lazy val annotated_ident = ident ~ opt(annotation) ^^ { case id ~ as => id + ∑(as) }
  
  //@todo ident w/o an assignment to an constant is automatically handled as boolean and set to true
  private lazy val annotation = "[" ~> ((ident <~ "=") ~ constant) ~ (("," ~> ((ident <~ "=") ~ constant))*) <~ "]" ^^ {
    case hd ~ tl => ((hd :: tl) map {case k ~ v => k+"="+v}) mkString ("[", ",", "]")
  }
  
  override lazy val string = accept("string",  {case lexical.StringLit(n) => n mkString ("\"", "", "\"") })
}
